﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PaceR.Core.Interfaces.Validation;
using PaceR.Core.Interfaces;
using PaceR.Core.Interfaces.Conditionals;
using PaceR.Core.Utilities;
using PaceR.Core.Conditionals;

namespace PaceR.Core.Logic
{
    public class SimpleLogicRule : IConditionRule, IConditionRuleContainer
    {
        /// <summary>
        /// protected method to force use of static Define method
        /// </summary>
        protected SimpleLogicRule()
        { }

        public static IConditionRuleContainer Define()
        {
            return new SimpleLogicRule();
        }

        #region IConditionRule Members

        public virtual IConditionTestable Test { get; set; }

        public virtual IConditionalOperation SuccessOperation { get; set; }

        public virtual IConditionalOperation FailureOperation { get; set; }

        public virtual IConditionRule If(IConditionTestable test, IConditionalOperation operation)
        {
            Test = test;
            SuccessOperation = operation;
            return this;
        }

        public virtual IConditionRule If<T, TResult>(IConditionTestable test, Func<T, TResult> thenOperation, T argument)
        {
            return If(test, BuildOperation(thenOperation, argument));
        }

        public virtual IConditionRule If<T>(IConditionTestable test, Action<T> thenOperation, T argument)
        {
            return If(test, BuildOperation(thenOperation, argument));
        }

        public virtual IConditionRule ElseIf(IConditionTestable test, IConditionalOperation operation)
        {
            SimpleLogicRule rule = new SimpleLogicRule()
            {
                Test = test,
                SuccessOperation = operation
            };
            FailureOperation = rule;
            return rule;
        }

        public virtual IConditionRule ElseIf<T, TResult>(IConditionTestable test, Func<T, TResult> thenOperation, T argument)
        {
            return ElseIf(test, BuildOperation(thenOperation, argument));
        }

        public IConditionRule ElseIf<T>(IConditionTestable test, Action<T> thenOperation, T argument)
        {
            return ElseIf(test, BuildOperation(thenOperation, argument));
        }

        public virtual IConditionRule Else(IConditionalOperation operation)
        {
            FailureOperation = operation;
            return this;
        }

        public IConditionRule Else<T, TResult>(Func<T, TResult> elseOperation, T argument)
        {
            return Else(BuildOperation(elseOperation, argument));
        }

        public IConditionRule Else<T>(Action<T> elseOperation, T argument)
        {
            return Else(BuildOperation(elseOperation, argument));
        }

        #endregion

        #region IConditionalOperation Members

        public virtual IResultMessages Execute()
        {
            IResultMessages messages = ResultObjectFactoryUtil.Factory.GetResultMessagesObject();
            if (Test.RunTest())
            {
                //Test passed run Success Operation
                if (SuccessOperation != null)
                    messages.MergeMessages(SuccessOperation.Execute());
            }
            else if (FailureOperation != null)
            {
                // Test failed run Failure Operation
                messages.MergeMessages(FailureOperation.Execute());
            }
            //return
            return messages;
        }

        #endregion

        protected virtual IConditionalOperation BuildOperation<T, TResult>(Func<T, TResult> function, T argument)
        {
            return new SimpleConditionalFunction<T, TResult>()
            {
                Function = function,
                FunctionArgument = argument
            };
        }

        protected virtual IConditionalOperation BuildOperation<T>(Action<T> action, T argument)
        {
            return new SimpleConditionalAction<T>()
            {
                Action = action,
                ActionArgument = argument
            };
        }
    }
}
